#include <string>
#include <string.h>
#include <iostream>
#include <filesystem>
#include <fstream>
#include "Config.h"
#include "CtpMd.h"

// ---- 全局变量 ---- //
// 公共参数
TThostFtdcBrokerIDType gBrokerID = "9999";     // 模拟经纪商代码
TThostFtdcInvestorIDType gInvesterID = "";     // 投资者账户名
TThostFtdcPasswordType gInvesterPassword = ""; // 投资者密码

// 行情参数
CThostFtdcMdApi *g_pMdUserApi = nullptr;                                 // 行情指针
char gMdFrontAddr[] = "tcp://180.168.146.187:10131";                     // 模拟行情前置地址
const char *g_pInstrumentID[] = {"TF1706", "zn1705", "cs1801", "CF705"}; // 行情合约代码列表，中、上、大、郑交易所各选一种
int instrumentNum = 4;

namespace fs = std::filesystem;

bool checkAndCreateDir(const fs::path &dir_path)
{
    if (!fs::exists(dir_path))
    {
        fs::create_directory(dir_path);
        std::cout << "Directory created at: " << dir_path << std::endl;
        return true;
    }
    else
    {
        std::cout << "Directory already exists at: " << dir_path << std::endl;
        return true;
    }
}

MdSpi::MdSpi(CThostFtdcMdApi *pMdApi, std::string path)
{
    // // 创建行情API实例
    // m_pMdUserApi = CThostFtdcMdApi::CreateFtdcMdApi();
    __pMdUserApi = pMdApi;
    __savePath = path;
}

MdSpi::~MdSpi()
{
}

// 当客户端与交易后台建立起通信连接时的回调函数
void MdSpi::OnFrontConnected()
{
    std::cout << "建立网络连接成功" << std::endl; // 输出网络连接成功消息

    // 创建登录请求数据结构
    CThostFtdcReqUserLoginField loginReq;
    memset(&loginReq, 0, sizeof(loginReq)); // 将结构体初始化为0
    // 填充登录信息
    strcpy(loginReq.BrokerID, MyBrokerID);
    strcpy(loginReq.UserID, MyUserID);
    strcpy(loginReq.Password, MyPassword);
    strcpy(loginReq.UserProductInfo, MyUserProductInfo);
    static int requestID = 0; // 请求编号
    // 发送登录请求
    int result = __pMdUserApi->ReqUserLogin(&loginReq, ++requestID);
    if (!result)
        std::cout << "发送登录请求成功" << std::endl; // 输出登录请求成功消息
    else
        std::cerr << "发送登录请求失败" << std::endl; // 输出登录请求失败消息
    // std::cout << "------当前版本号 :" << __pMdUserApi->GetApiVersion() << " ------" << std::endl; // 输出当前API版本号
}

void MdSpi::OnRspUserLogin(
    CThostFtdcRspUserLoginField *pRspUserLogin,
    CThostFtdcRspInfoField *pRspInfo,
    int nRequestID, bool bIsLast)

{
    bool bResult = pRspInfo && (pRspInfo->ErrorID != 0); // 判断登录是否成功
    if (!bResult)
    {
        std::cout << "账户登录成功" << std::endl;                           // 输出账户登录成功消息
        std::cout << "交易日： " << pRspUserLogin->TradingDay << std::endl; // 输出交易日信息
        __tradingDay = pRspUserLogin->TradingDay;                           // 记录交易日信息
        // 开始订阅行情
        const char *ppInstrumentID[] = SubscribeMarketDatas;                                                            // 获取待订阅合约数组
        __pMdUserApi->SubscribeMarketData((char **)ppInstrumentID, sizeof(ppInstrumentID) / sizeof(ppInstrumentID[0])); // 发送订阅行情请求
    }
    else
        std::cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << std::endl; // 输出登录失败消息
}
void MdSpi::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
    // 检查响应信息是否存在错误
    bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
    if (!bResult)
    {
        // 如果订阅成功
        std::cout << "订阅行情成功" << std::endl;
        std::cout << "合约代码： " << pSpecificInstrument->InstrumentID << std::endl;

        // 检查是否需要存储行情数据
        if (checkAndCreateDir(__savePath) && checkAndCreateDir(__savePath + "/" + pSpecificInstrument->InstrumentID))
        {
            // 如果需要存储数据，创建目录
            std::cout << __savePath + "/" + pSpecificInstrument->InstrumentID + "创建成功" << std::endl;
        }
        else
        {
            // 创建目录失败，退出程序
            std::cout << __savePath + "/" + pSpecificInstrument->InstrumentID + "创建失败" << std::endl;
            exit(1);
        }

        // 设置保存文件的路径
        __saveFiles[pSpecificInstrument->InstrumentID] = __savePath + "/" + pSpecificInstrument->InstrumentID + "/" + __tradingDay + ".csv";
        // 已写入的数据数目
        __saveDataNums[pSpecificInstrument->InstrumentID] = 0;

        // 创建并写入文件表头
        std::ofstream outFile;
        outFile.open(__saveFiles[pSpecificInstrument->InstrumentID], std::ios::out | std::ios::binary);
        outFile << "交易日,合约代码,交易所代码,最新价,数量,成交金额,持仓量,最后修改时间,最后修改毫秒,申买价一,申买量一,申卖价一,申卖量一" << std::endl;
        outFile.close();
    }
    else
    {
        // 如果订阅失败，输出错误信息
        std::cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << std::endl;
    }
}

void MdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData)
{
#ifdef PRINT_DATA
    // 打印行情信息
    std::cout << "=====获得深度行情=====" << std::endl;
    std::cout << "交易日： " << pDepthMarketData->TradingDay << std::endl;                 // 获取交易日
    std::cout << "交易所代码： " << pDepthMarketData->ExchangeID << std::endl;             // 获取交易所代码
    std::cout << "合约代码： " << pDepthMarketData->InstrumentID << std::endl;             // 获取合约代码
    std::cout << "合约在交易所的代码： " << pDepthMarketData->ExchangeInstID << std::endl; // 获取合约在交易所的代码
    std::cout << "最新价： " << pDepthMarketData->LastPrice << std::endl;                  // 获取最新价
    std::cout << "数量： " << pDepthMarketData->Volume << std::endl;                       // 获取数量
#endif
    std::cout << pDepthMarketData->InstrumentID << "已采集数据量:" << ++__saveDataNums[pDepthMarketData->InstrumentID] << std::endl;

    // 将行情写入到CSV文件中
    // 如果只获取某一个合约的行情，可以逐tick地存入文件或数据库
    std::ofstream outFile;
    outFile.open(__saveFiles[pDepthMarketData->InstrumentID], std::ios::app); // 以追加写入方式打开文件
    outFile << pDepthMarketData->TradingDay << ","                            // 写入交易日
            << pDepthMarketData->InstrumentID << ","                          // 写入合约代码
            << pDepthMarketData->ExchangeID << ","                            // 写入交易所代码
            << pDepthMarketData->LastPrice << ","                             // 写入最新价
            << pDepthMarketData->Volume << ","                                // 写入数量
            << pDepthMarketData->Turnover << ","                              // 写入成交金额
            << pDepthMarketData->OpenInterest << ","                          // 写入持仓量
            << pDepthMarketData->UpdateTime << ","                            // 写入最后修改时间
            << pDepthMarketData->UpdateMillisec << ","                        // 写入最后修改毫秒
            << pDepthMarketData->BidPrice1 << ","                             // 写入申买价一
            << pDepthMarketData->BidVolume1 << ","                            // 写入申买量一
            << pDepthMarketData->AskPrice1 << ","                             // 写入申卖价一
            << pDepthMarketData->AskVolume1 << std::endl;                     // 写入申卖量一
    outFile.close();                                                          // 关闭文件流
}